package edu.ucsd.cse110;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.InputFilter;
import android.text.Spanned;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import edu.ucsd.cse110.model.Log;
import edu.ucsd.cse110.model.Wod;

/**
 * Allows user to log their workout entry.
 */
public class LogbookFragment extends Fragment implements OnClickListener {
	/**
	 * Class that filters out input in the seconds textfield.
	 */
	public class InputFilterMinMax implements InputFilter {
		private int min, max;

		/**
		 * Constructor that takes in the min and max value as ints.
		 * 
		 * @param min
		 *            the minimum value that the number inputted can be.
		 * @param max
		 *            the maximum value that the number inputted can be.
		 */
		public InputFilterMinMax(int min, int max) {
			this.min = min;
			this.max = max;
		}

		/**
		 * Constructor that takes in the min and max value as Strings.
		 * 
		 * @param min
		 *            the minimum value that the number inputted can be.
		 * @param max
		 *            the maximum value that the number inputted can be.
		 */
		public InputFilterMinMax(String min, String max) {
			this.min = Integer.parseInt(min);
			this.max = Integer.parseInt(max);
		}

		/**
		 * This filters input using the given min and max values.
		 * 
		 * @see android.text.InputFilter#filter(java.lang.CharSequence, int,
		 *      int, android.text.Spanned, int, int)
		 */
		public CharSequence filter(CharSequence source, int start, int end,
				Spanned dest, int dstart, int dend) {
			try {
				String startString = dest.toString().substring(0, dstart);
				String insert = source.toString();
				String endString = dest.toString().substring(dend);
				String parseThis = startString + insert + endString;
				int input = Integer.parseInt(parseThis);
				if (isInRange(min, max, input)) {
					return null;
				}
			} catch (NumberFormatException nfe) {
			}
			return "";
		}

		/**
		 * Used to determine if the given number is within range.
		 */
		private boolean isInRange(int a, int b, int c) {
			return b > a ? (c >= a) && (c <= b) : (c >= b) && (c <= a);
		}
	}

	/**
	 * TODO: Need documentation.
	 */
	private class MyTextWatcher implements TextWatcher {
		/**
		 * TODO: Need documentation.
		 */
		public void afterTextChanged(Editable arg0) {
			Button saveBtn = (Button) view.findViewById(R.id.logbook_btn_save);
			saveBtn.setEnabled(true);
			saveBtn.setBackgroundResource(R.drawable.custom_button);
		}

		/**
		 * TODO: Need documentation.
		 */
		public void beforeTextChanged(CharSequence s, int start, int count,
				int after) {
		}

		/**
		 * TODO: Need documentation.
		 */
		public void onTextChanged(CharSequence s, int start, int before,
				int count) {
		}
	}

	private Button btnWodSelection;

	/**
	 * Constants that are used in setView(). pass these values in to determine
	 * what view will be set.
	 */
	private final int EDIT_VIEW = 1;

	/**
	 * Holds fields for the logbook screen for later use.
	 */
	private EditText etMinute, etSecond, etBodyWeight, etRep, etRound, etNote;
	private boolean facebookIconVisible = false;
	private CheckBox injury;

	/**
	 * Length notifications show up for.
	 */
	private final int LENGTH = 2000;

	/**
	 * Holds the view, the currently selected wod, and the current log.
	 */
	private Log mLog;

	private final int NEW_VIEW = 2;

	private final int REQUEST_CODE_WOD_SELECTION = 1;
	private final int REQUEST_CODE_DATE_SELECTION = 2;

	private TextView txtDate;

	private View view;

	/**
	 * Creates a new log entry.
	 */
	private void createNewLog() {
		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
		builder.setMessage(
				"This will create an additional log for the current date.")
				.setCancelable(false)
				.setPositiveButton("Create",
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								Calendar c = Calendar.getInstance();
								c.clear();
								c.set(Calendar.YEAR,
										mLog.getDate().get(Calendar.YEAR));
								c.set(Calendar.MONTH,
										mLog.getDate().get(Calendar.MONTH));
								c.set(Calendar.DATE,
										mLog.getDate().get(Calendar.DATE));
								newLogbook(c);
								displayLog();

								final Dialog d = new Dialog(getActivity());
								d.setTitle("New Log Created");
								d.show();
								final Handler handler = new Handler();
								handler.postDelayed(new Runnable() {
									public void run() {
										d.dismiss();
									}
								}, LENGTH);
								View newLogBtn = view
										.findViewById(R.id.logbook_btn_new);
								newLogBtn.setVisibility(View.INVISIBLE);

							}
						})
				.setNegativeButton("Cancel",
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								dialog.cancel();
							}
						});
		builder.create().show();
	}

	/**
	 * Deletes an existing log entry.
	 */
	void deleteLog() {
		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
		builder.setMessage("This will delete the current log.")
				.setCancelable(false)
				.setPositiveButton("Delete",
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								LocalDatabaseManager db = new LocalDatabaseManager(
										getActivity());
								db.deleteLogById(mLog.getId());
								db.close();
								Calendar c = Calendar.getInstance();
								c.clear();
								c.set(Calendar.YEAR, Calendar.getInstance()
										.get(Calendar.YEAR));
								c.set(Calendar.MONTH, Calendar.getInstance()
										.get(Calendar.MONTH));
								c.set(Calendar.DATE, Calendar.getInstance()
										.get(Calendar.DATE));
								newLogbook(c);
								displayLog();

								dialog.cancel();
								final Dialog d = new Dialog(getActivity());
								d.setTitle("Log Successfully Deleted");
								d.show();
								final Handler handler = new Handler();
								handler.postDelayed(new Runnable() {
									public void run() {
										d.dismiss();
									}
								}, LENGTH);

							}
						})
				.setNegativeButton("Cancel",
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								dialog.cancel();
							}
						});
		builder.create().show();
	}

	/**
	 * Takes the current mLog and displays the information contained within it
	 * to the screen. If mLog's ID is 0, then display a blank screen, since this
	 * is a new logbook entry. If mLog's ID is not 0, then this is an already
	 * existing logbook entry, so mLog will be displayed on the screen.
	 */
	void displayLog() {
		if (mLog.getId() == 0) {

			txtDate.setText(getDateString(mLog.getDate()) + " | NEW LOG");
			etNote.setText("");
			etRound.setText("");
			etRep.setText("");
			etMinute.setText("");
			etSecond.setText("");
			etBodyWeight.setText("");
			injury.setChecked(false);
			btnWodSelection.setText("WOD Selection");
		} else {
			LocalDatabaseManager db = new LocalDatabaseManager(getActivity());
			List<Log> logList = db.getLog(String.format("%s = ?",
					Log.COLUMNS[1]), new String[] { Long.toString(mLog
					.getDate().getTimeInMillis()) }, null, null, null);
			int index = 0;
			for (Log l : logList) {
				index++;
				if (l.getId() == mLog.getId()) {
					break;
				}
			}

			setEnabled();

			txtDate.setText(getDateString(mLog.getDate()) + " | Entry " + index);
			etNote.setText(mLog.getNotes());
			etRound.setText("" + mLog.getRound());
			etRep.setText("" + mLog.getRep());
			etMinute.setText("" + mLog.getMinute());
			etSecond.setText("" + mLog.getSecond());
			etBodyWeight.setText("" + mLog.getBodyWeight());
			injury.setChecked(mLog.getIsInjured());
			if (mLog.getWodId() != 0) {
				Wod w = db.getWodById(mLog.getWodId()).get(0);
				btnWodSelection.setText("" + w.getName());
			} else {
				btnWodSelection.setText("WOD Selection");
			}

			db.close();
		}
	}

	/**
	 * Gets current date as a string, formatted for use to display on the screen
	 * for the user to look at.
	 * 
	 * @return Current date as a string.
	 */
	private String getDateString(Calendar c) {
		SimpleDateFormat sdf = new SimpleDateFormat("EEEE, MMMM dd");
		String strdate = null;
		if (c != null) {
			strdate = sdf.format(c.getTime());
		}
		return strdate;
	}

	/**
	 * Creates a new log, initialized with the given calendar date.
	 * 
	 * @param c
	 *            the new date that this logbook is meant for.
	 */
	private void newLogbook(Calendar c) {
		// Creates a brand new log. ID is initially 0.
		mLog = new Log();
		mLog.setId(0);

		// Initializes date in the empty log.
		mLog.setDate(c);

		etRound.setEnabled(true);
		etSecond.setEnabled(true);
		etMinute.setEnabled(true);
		etRep.setEnabled(true);

		setView(NEW_VIEW);
	}

	/**
	 * Contains the code that will be executed once returned from a particular
	 * activity.
	 * 
	 * @see android.support.v4.app.Fragment#onActivityResult(int, int,
	 *      android.content.Intent)
	 */
	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		LocalDatabaseManager database = new LocalDatabaseManager(getActivity());

		switch (requestCode) {
		// Called after selecting a WOD. Displays the WOD name on the button.
		// also sets the log's wod id to the wod selected.
		case REQUEST_CODE_WOD_SELECTION:
			if (resultCode == Activity.RESULT_OK) {
				mLog.setWodId(data.getExtras().getInt("Id"));
				Wod d = database.getWodById(mLog.getWodId()).get(0);
				btnWodSelection.setText(d.getName());
				setEnabled();
			}
			break;
		// Called after selecting a date in the calendar.
		case REQUEST_CODE_DATE_SELECTION:
			// This is executed if the log already exists.
			if (resultCode == 1) {
				mLog = database.getLogById(data.getExtras().getInt("id"))
						.get(0);
				setView(EDIT_VIEW);
				View newLogBtn = view.findViewById(R.id.logbook_btn_new);
				newLogBtn.setVisibility(View.VISIBLE);
				displayLog();
			}

			// This is executed if the log does not exist; a new log is
			// created here and displayed.
			if (resultCode == 2) {
				mLog = new Log();
				Calendar c = Calendar.getInstance();
				c.clear();
				c.set(Calendar.YEAR, data.getExtras().getInt("year"));
				c.set(Calendar.MONTH, data.getExtras().getInt("month"));
				c.set(Calendar.DATE, data.getExtras().getInt("day"));
				View newLogBtn = view.findViewById(R.id.logbook_btn_new);
				newLogBtn.setVisibility(View.INVISIBLE);
				newLogbook(c);
				displayLog();
			}
			break;
		}

		database.close();
	}

	/**
	 * Sets up the methods that will get called once a button is pressed.
	 */
	public void onClick(View v) {
		switch (v.getId()) {

		// Calls the activity that will allow sharing on facebook.
		case R.id.logbook_img_facebook:
			if (!facebookIconVisible) {
				deleteLog();
			} else {
				shareOnFacebook();
			}
			break;

		// Calls the activity that allows the user to select a different log.
		case R.id.logbook_img_calendar:
			super.startActivityForResult(new Intent(getActivity(),
					CalendarActivity.class), REQUEST_CODE_DATE_SELECTION);
			break;

		// Calls the activity that allows the user to select a WOD.
		case R.id.logbook_btn_wod:
			super.startActivityForResult(new Intent(getActivity(),
					WodSelectionActivity.class), REQUEST_CODE_WOD_SELECTION);
			break;

		// Calls the activity that displays the WOD Description.
		// Only let the user select when the current log has a wod.
		case R.id.logbook_txt_description:
			if (mLog.getWodId() != 0) {
				Intent intent = new Intent(getActivity(),
						BenchmarkDetailActivity.class);
				intent.putExtra("Id", mLog.getWodId());

				LocalDatabaseManager mDatabase = new LocalDatabaseManager(
						getActivity());
				// Wod mWod = mDatabase.getWodById(mLog.getWodId()).get(0);
				mDatabase.close();
				intent.putExtra("Type", "");// mWod.getType());

				super.startActivity(intent);
			}
			break;

		// Calls the activity that allows the user to save the current log.
		case R.id.logbook_btn_save:
			saveLog();
			break;

		case R.id.logbook_btn_new:
			createNewLog();
			break;
		}
	}

	/**
	 * Called to have the fragment instantiate its user interface view. Creates
	 * a new logbook, sets up listeners, and displays default values.
	 */
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

		// Initializing the view layout.
		if (container == null) {
			return null;
		}
		view = inflater.inflate(R.layout.logbook, container, false);

		// Keeps a reference to the views of these buttons and text fields.
		etMinute = (EditText) view.findViewById(R.id.logbook_et_minute);
		etSecond = (EditText) view.findViewById(R.id.logbook_et_second);
		etBodyWeight = (EditText) view.findViewById(R.id.logbook_et_bodyweight);
		etRep = (EditText) view.findViewById(R.id.logbook_et_rep);
		etRound = (EditText) view.findViewById(R.id.logbook_et_round);
		etNote = (EditText) view.findViewById(R.id.logbook_et_note);
		txtDate = (TextView) view.findViewById(R.id.logbook_txt_date);
		injury = (CheckBox) view.findViewById(R.id.injuryCheckBox);
		btnWodSelection = (Button) view.findViewById(R.id.logbook_btn_wod);

		injury.setOnCheckedChangeListener(new OnCheckedChangeListener() {
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {
				Button saveBtn = (Button) view
						.findViewById(R.id.logbook_btn_save);
				saveBtn.setEnabled(true);
				saveBtn.setBackgroundResource(R.drawable.custom_button);
			}

		});
		etMinute.addTextChangedListener(new MyTextWatcher());
		etSecond.addTextChangedListener(new MyTextWatcher());
		etBodyWeight.addTextChangedListener(new MyTextWatcher());
		etRep.addTextChangedListener(new MyTextWatcher());
		etRound.addTextChangedListener(new MyTextWatcher());
		etNote.addTextChangedListener(new MyTextWatcher());

		// Initializes date in the empty log. The date is the current date.
		Calendar c = Calendar.getInstance();
		c.clear();
		c.set(Calendar.YEAR, Calendar.getInstance().get(Calendar.YEAR));
		c.set(Calendar.MONTH, Calendar.getInstance().get(Calendar.MONTH));
		c.set(Calendar.DATE, Calendar.getInstance().get(Calendar.DATE));
		newLogbook(c);
		displayLog();

		// Sets up the seconds text field. Uses a filter.
		etSecond.setFilters(new InputFilter[] { new InputFilterMinMax("0", "59") });

		// Sets up all of the listeners for classes.
		View wodSelectionButton = view.findViewById(R.id.logbook_btn_wod);
		wodSelectionButton.setOnClickListener(this);
		ImageView facebookImage = (ImageView) view
				.findViewById(R.id.logbook_img_facebook);
		facebookImage.setOnClickListener(this);
		ImageView calendarImage = (ImageView) view
				.findViewById(R.id.logbook_img_calendar);
		calendarImage.setOnClickListener(this);
		View wodDescriptionText = view
				.findViewById(R.id.logbook_txt_description);
		wodDescriptionText.setOnClickListener(this);
		View sqlSave = view.findViewById(R.id.logbook_btn_save);
		sqlSave.setOnClickListener(this);
		View newLogBtn = view.findViewById(R.id.logbook_btn_new);
		newLogBtn.setOnClickListener(this);
		newLogBtn.setVisibility(View.INVISIBLE);

		return view;
	}

	/**
	 * Saves the current log entry into database.
	 */
	private void saveLog() {
		// Used to determine if an exception was thrown.
		boolean isWorking = true;
		// Used to determine if the user updated or made a new
		// entry.
		boolean didUpdate = false;

		try {
			LocalDatabaseManager database = new LocalDatabaseManager(
					getActivity());

			// Retrieve all data from the individual fields.
			if (!etBodyWeight.getText().toString().equals("")) {
				mLog.setBodyWeight(Integer.parseInt(etBodyWeight.getText()
						.toString()));
			}
			if (!etMinute.getText().toString().equals("")
					&& !etSecond.getText().toString().equals("")) {
				mLog.setTime(Integer.parseInt(etMinute.getText().toString()),
						Integer.parseInt(etSecond.getText().toString()));
			}
			if (!etRep.getText().toString().equals("")) {
				mLog.setRep(Integer.parseInt(etRep.getText().toString()));
			}
			if (!etRound.getText().toString().equals("")) {
				mLog.setRound(Integer.parseInt(etRound.getText().toString()));
			}
			if (!etNote.getText().toString().equals("")) {
				mLog.setNotes(etNote.getText().toString());
			}
			mLog.setIsInjured(injury.isChecked());

			if (mLog.getId() != 0) {
				database.updateLog(mLog);
				didUpdate = true;

				List<Log> logList = database.getLog(String.format("%s = ?",
						Log.COLUMNS[1]), new String[] { Long.toString(mLog
						.getDate().getTimeInMillis()) }, null, null, null);
				int index = 0;
				for (Log l : logList) {
					index++;
					if (l.getId() == mLog.getId()) {
						break;
					}
				}
				txtDate.setText(getDateString(mLog.getDate()) + " | Entry "
						+ index + " (SAVED)");

			} else {
				int id = (int) database.insertLog(mLog);
				mLog.setId(id);
				txtDate.setText(getDateString(mLog.getDate())
						+ " | NEW LOG (SAVED)");
			}
			database.close();
			Button saveBtn = (Button) view.findViewById(R.id.logbook_btn_save);
			saveBtn.setEnabled(false);
			saveBtn.setBackgroundResource(R.drawable.custom_button_past);

			View newLogBtn = view.findViewById(R.id.logbook_btn_new);
			newLogBtn.setOnClickListener(this);
			newLogBtn.setVisibility(View.VISIBLE);
		} catch (Exception e) {
			isWorking = false;
			String error = e.getStackTrace().toString();
			Dialog d = new Dialog(getActivity());
			d.setTitle("OH NO!");
			TextView tv = new TextView(getActivity());
			tv.setText(error);
			d.setContentView(tv);
			d.show();
		} finally {
			if (isWorking) {
				final Dialog d = new Dialog(getActivity());
				if (didUpdate) {
					d.setTitle("Entry Successfully Updated.");
				} else {
					d.setTitle("Entry Successfully Added.");
				}
				d.show();
				final Handler handler = new Handler();
				handler.postDelayed(new Runnable() {
					public void run() {
						d.dismiss();
					}
				}, LENGTH);

			}
		}
	}

	/**
	 * TODO: Need documentation.
	 */
	private void setEnabled() {
		LocalDatabaseManager db = new LocalDatabaseManager(getActivity());
		if (mLog.getWodId() == 0) {
			etRound.setEnabled(true);
			etRep.setEnabled(true);
			etSecond.setEnabled(true);
			etMinute.setEnabled(true);
			return;
		}
		Wod wod = db.getWodById(mLog.getWodId()).get(0);
		Wod.Type type = wod.getType();
		int filterInts[] = null;
		// String filterStrings[] = null;
		switch (type) {
		case Girl:
			filterInts = getResources().getIntArray(
					R.array.girl_benchmarks_filter);
			break;
		case Hero:
			filterInts = getResources().getIntArray(
					R.array.hero_benchmarks_filter);
			break;
		case Other:
			filterInts = getResources().getIntArray(
					R.array.other_benchmarks_filter);
			break;

		case Custom:
			// build filterInts array with the getPRType() call to WOD objects
			// in returned list
			List<Wod> theList = db.getWodById(mLog.getWodId());
			filterInts = new int[theList.size()];
			for (int i = 0; i < theList.size(); i++) {
				filterInts[i] = theList.get(i).getPRType().ordinal();
			}
			break;
		}

		db.close();
		switch (wod.getPRType().ordinal()) {
		case 0:
			etRound.setText("");
			etRound.setEnabled(false);
			etRep.setText("");
			etRep.setEnabled(false);
			etSecond.setEnabled(true);
			etMinute.setEnabled(true);
			break;
		case 1:
			etRep.setEnabled(true);
			etSecond.setEnabled(false);
			etSecond.setText("");
			etMinute.setEnabled(false);
			etMinute.setText("");
			etRound.setEnabled(false);
			etRound.setText("");
			break;
		case 2:
			etRound.setEnabled(true);
			etSecond.setEnabled(false);
			etSecond.setText("");
			etMinute.setEnabled(false);
			etMinute.setText("");
			etRep.setEnabled(false);
			etRep.setText("");
			break;
		}
	}

	/**
	 * Sets the image icon on the bottom-left of logbook screen.
	 */
	private void setView(int view) {
		ImageView Image = (ImageView) this.view
				.findViewById(R.id.logbook_img_facebook);

		if (view == NEW_VIEW) {
			Image.setImageResource(R.drawable.facebook_icon);
			facebookIconVisible = true;
		}
		if (view == EDIT_VIEW) {
			Image.setImageResource(R.drawable.delete_icon);
			facebookIconVisible = false;
		}
	}

	/**
	 * Shares the log entry on Facebook.
	 */
	private void shareOnFacebook() {
		Intent intent = new Intent(getActivity(), ShareOnFacebookActivity.class);
		intent.putExtra("link", "http://acsweb.ucsd.edu/~whleung/");
		intent.putExtra("name", "droidFit");
		intent.putExtra("picture", "http://i.imgur.com/UbUe7.png");

		Log data = new Log();

		data.setDate(mLog.getDate());

		// Retrieve all data from the individual fields.
		if (!etBodyWeight.getText().toString().equals("")) {
			data.setBodyWeight(Integer.parseInt(etBodyWeight.getText()
					.toString()));
		}
		if (!etMinute.getText().toString().equals("")
				&& !etSecond.getText().toString().equals("")) {
			data.setTime(Integer.parseInt(etMinute.getText().toString()),
					Integer.parseInt(etSecond.getText().toString()));
		}
		if (!etRep.getText().toString().equals("")) {
			data.setRep(Integer.parseInt(etRep.getText().toString()));
		}
		if (!etRound.getText().toString().equals("")) {
			data.setRound(Integer.parseInt(etRound.getText().toString()));
		}
		if (etNote.length() == 0) {
			data.setNotes(etNote.getText().toString());
		}
		data.setIsInjured(injury.isChecked());

		if (mLog.getWodId() != 0) {
			LocalDatabaseManager db = new LocalDatabaseManager(getActivity());
			Wod wod = db.getWodById(mLog.getWodId()).get(0);
			db.close();

			String description = "";

			switch (wod.getPRType().ordinal()) {
			case 0:
				description = String.format(
						"I finsihed %s in %d minutes and %d seconds!",
						wod.getName(), data.getMinute(), data.getSecond());
				break;
			case 1:
				description = "I completed " + data.getRound() + " rounds of "
						+ wod.getName() + "!";
				break;
			case 2:
				description = "I completed " + data.getRep() + " reps of "
						+ wod.getName() + "!";
				break;
			}

			intent.putExtra("description", description);

			super.startActivity(intent);
		} else {
			Toast.makeText(getActivity(),
					"Did not post because WOD is not selected.", LENGTH).show();
		}
	}
}
